#ifndef CLASSE_H
#define CLASSE_H

#include <iostream>
#include <vector>
#include <map>
#include <set>

#include "debugstreams.h"
#include "lib.h"
#include "Sort.h"
#include "Prerequis.h"
#include "ListComp.h"
#include "ListEnsComp.h"
#include "ListCapa.h"

#include <boost/assign/list_of.hpp>

using namespace std;

/**
 * La classe Classe représente une classe au sens AD&D. C'est une
 * classe abstraite, qui sert au polymorphisme. Ici se fait la gestion
 * des diverses restrictions et compétences, qui sont lues depuis
 * chaque type de classe. L'objet Classe contient tous les attributs
 * et accesseurs communs aux différents types de classes.
 *
 * On ne stocke ici que des pointeurs sur les listes (et nombre) de
 * compétences et restrictions.
 *
 * @todo Réfléchir à la possibilité d'avoir des conteneurs plutôt que
 * des pointeurs. Cela faciliterait l'implémentation de MetaClasse.
 *
 * @author Johan "Solo" Luisier
 */
class Classe
{
 private:
  /**
   * Permet de déterminer si les niveaux de sorties ont bien été
   * initialisés.
   */
  static bool niveauxLus;
  /**
   * Liste des noms des niveaux de sorties paramétrables.
   */
  static const vector< string > NomsNiveaux;
  //static void lireNiveaux();
  /**
   * Sert à régler les problèmes de libération de mémoire ?!?
   */
  static const unsigned int Zero;
  /**
   * Est utilisé comme valeur de renvoi pour une compétence, un trait
   * ou le nombre de compétences lorsque le conteneur n'est pas
   * défini.
   */
  static const unsigned int pasTrouve;
 protected:
  /**
   * Nom des traits, utilisés pour l'affichage. L'attribut static est
   * utilisé par souci d'optimisation.
   */
  //static const vector< string > Caracteristiques;
  /*
   * Niveau, niveau magique et nombre de pts d'expérience.
   */
  unsigned int
    Niveau, /**< Niveau dans cette classe, calculé automatiquement. */
    Niveau_mag, /**< Niveau de magie dans cette classe, calculé
		   automatiquement. */
    PV, /**< Nombre de pt de vie pour cette classe. */
    XP; /**< Nombre de pt d'expérience dans cette classe. */
  /**
   * Valeurs des traits du personnage, le 7ème contient le score de
   * force exceptionnel (0 si non applicable).
   */
  vector< unsigned int > Traits;
  /**
   * Nom de la classe ("Rôdeur", "Barde", ...).
   */
  string Type_classe;
  /**
   * Nom de la Race, utilisé lors de la détermination des scores et
   * des traits.
   */
  string Type_race;
  /**
   * Liste des compétences que l'objet courant possède. Il s'agit de
   * la liste des \b numéros des compétences dans la liste de
   * l'instance. Si une compétences est associée à un pourcentage, ce
   * pourcentage est également stocké ici.
   */
  vector<unsigned int> Comp_classe;
  /**
   * Liste des sorts du livre de sort (Mages, Spécialistes et Bardes
   * uniquement).
   */
  vector<Sort> Cl_sorts;
  /**
   * Type de classe (TypeClasse) disponibles, détermine les
   * compétences non-martiales accessibles. Est déterminé d'après la
   * classe dérivée lors de la création de l'objet.
   *
   * @see TypeClasse
   */
  vector<TypeClasse> TC;
  /**
   * Niveau de sortie des fonctions globales, très simples. On peut
   * changer cette constante en modifiant la ligne Classe::elementaire
   * du fichier CONFIG.
   */
  static DebugLevelType elementaire;
  /**
   * Niveau de sortie des fonctions plus spécifiques de Classe. La
   * valeur de cette constante est définie par la ligne
   * Classe::fonction du fichier CONFIG.
   */
  static DebugLevelType fonction;
  /**
   * Nombre de points de personnages (PP) disponible pour la création
   * de la Classe.
   */
  unsigned int NombrePPsAlloues;
  /**
   * Nombre de compétences existantes.
   */
  unsigned int NombreTotalCompetences;
  /**
   * Nombre de restrictions existantes.
   */
  unsigned int NombreTotalRestrictions;
  /**
   * Nombre d'ensembles de compétences existants.
   */
  unsigned int NombreTotalEnsembles;
  /**
   * Nombre maximal de compétences dans un ensemble de compétences.
   */
  unsigned int NombreMaxCompetences;
  /**
   * Contient les compétences de classe dans la première "ligne". Si
   * la Classe possède des compétences "à tiroirs", ces compétences
   * sont dans les "lignes" suivantes.
   */
  vector< vector< ListComp > > ListesCompetences;
  /**
   * Contient les restrictions de classe.
   */
  vector< ListComp > ListeRestrictions;
  /**
   * Liste des ensembles de compétences.
   */
  vector< ListEnsComp > EnsemblesCompetences;
  /**
   * Liste des préreqis sur les traits pour la classe. Lorsqu'il
   * existe plusieurs ensembles avec des prérequis variables, ils sont
   * alors écrits sur plusieurs "lignes".
   */
  vector< vector< Prerequis > > PrerequisClasse;
  /**
   * Liste des Races admises pour la Classe. Lorsqu'il existe
   * plusieurs ensembles avec des prérequis variables, ils sont alors
   * écrits sur plusieurs "lignes".
   */
  vector< set< string > > RacesAdmisesClasse;
  /**
   * Calcule le niveau du personnage, en fonction de sa classe et de son
   * nombre de pts d'expérience.
   */
  virtual void calculNiveau() = 0;
  /**
   * Calcule le TAC0 du personnage
   */
  virtual unsigned int calculeTAc0() const = 0;
  /**
   * Calcule le niveau de magie du personnage, en fonction de sa classe
   * et de son niveau (recalculé par précaution). Si le personnage n'a pas
   * de sorts ou a un niveau insuffisant pour en lancer, le niveau magique
   * calculé est 0.
   */
  virtual void calculNivMag() = 0;
 public:
  Classe();
  Classe(const unsigned int& xp, const vector< unsigned int >& trait,
	 const TypeClasse& tc,
	 const string& race);
  Classe(const Classe& classe);
  virtual ~Classe();
  void setCompetences(const vector<unsigned int>& capa);
  void setTraits(const vector< unsigned int >& traits);
  void affiche() const;
  unsigned int calculeNombres();
  void afficherStats(ostream& os = blank());
  void afficheCompetences(ostream& os = blank()) const;
  const string& getClasse() const;
  const unsigned int& getPV() const;
  const unsigned int& getXP() const;
  void setTrait(const unsigned int& i, const unsigned int& val);
  const unsigned int& getTrait(const unsigned int& i) const;
  const vector< unsigned int >* getTraits() const;
  unsigned int getCombien() const;
  const unsigned int& getComp(const unsigned int& i) const;
  virtual void appliquerModif();
  virtual void augmenterTalents(const bool& lvl_up = true);
  /**
   * Calcule les JS du personnage et les mets dans le tableau passé en
   * argument. Si le personnage a plusieurs classes, les JS les plus
   * avantageux seront automatiquement sélectionnés dans la classe
   * Joueur.
   *
   * @param tab pointeur sur le tableau contenant les JS.
   */
  virtual void getJS(unsigned int *tab) const = 0;
  string getCompetence(const unsigned int& i, bool& sauter,
		       const bool& LaTeX = false) const;
  virtual unsigned int getValeurBase(const unsigned int& i) const;
  const unsigned int& getNiveau() const;
  const unsigned int& getNiveauMagie() const;
  virtual void ecrireSorts(const string& nom_fichier, const bool& ecrase) const;
  virtual void nbrSort(unsigned int sorts[], const unsigned int& taille) const;
  string supprimeAccents(const string& brut) const;
  const vector<TypeClasse>* getTC() const;
  void augmenterXP(const unsigned int& xp);
  void augmenterPV(const unsigned int& pv);
  static void setLevel(const unsigned int& level, const unsigned int& n);
  virtual void ajoutSorts();
  void ajoutSorts(const vector<Sort>& vec);
  void setSorts(const vector<Sort>& sorts);
  void setPV(const unsigned int& pv);
  void setXP(const unsigned int& xp);
  unsigned int getCombienSorts() const;
  Sort getSort(const unsigned int& i) const;
  const vector<Sort>* getSortAdresse() const;
  void getCompetences(unsigned int& nbrComp, vector<ListComp>& comp, unsigned int& nbrRes, vector<ListComp>& restric) const;
  const vector< ListComp >* getCompetences(const unsigned int& num = 0) const;
  virtual void getDonneesEnsCompetences(unsigned int& nbr, unsigned int& max) const;
  //virtual const vector<unsigned int>* getEnsembleCompetences(const unsigned int& nbr) const;
  /* const string* getNomEnsemble(const unsigned int& nbr) const; */
  /* const unsigned int* getPrixEnsemble(const unsigned int& nbr) const; */
  //const ListEnsComp* getEnsemble(const unsigned int& nbr) const;
  const vector< ListEnsComp >* getEnsembles() const;
  const unsigned int& getPP() const;
  /**
   * Fait correspondre le prix d'accès à une compétence martiale en
   * fonction de la classe du personnage.
   *
   * @param Map pointeur sur la map qui sera la table de
   * correspondance.
   */
  virtual void getPrixAcces(map<string, unsigned int>*& Map) const = 0;
  const vector<unsigned int>* getToutesCompetences(unsigned int& nbr) const;
  ListComp getCompetenceNumero(const unsigned int& i) const;
  bool possedePourcent() const;
  virtual unsigned int getPourcent(const bool& lvlUp) const;
  virtual bool forceExceptionnelle() const;
  virtual string getScoreAdditionnel(const unsigned int& i) const;
  void construitClasse(unsigned int& pps);
  const unsigned int* getNbrEns() const;
  const unsigned int* getMaxEns() const;
  virtual StatusCode prerequisOK(const string& race,
				 const unsigned int& ensemble = -1u);
};

#endif
